home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Games of Daze
/
Infomagic - Games of Daze (Summer 1995) (Disc 1 of 2).iso
/
x2ftp
/
msdos
/
misc
/
moonbas2
/
vga_y.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
1995-04-11
|
15KB
|
587 lines
#include <stdlib.h>
#include <string.h>
#include <conio.h>
#include <dos.h>
#include "boolean.h"
#include "vga.h"
//#define VSYNC_MONITOR // This puts a bar graph on the screen that shows
// how much time is being spent waiting for
// vertical retrace.
char * const screen_lin_addr = (char *) 0xA0000;
const video_int = 0x10; // Video interrupt number
const sequ_addr = 0x3c4; // Base port of Sequencer
const crtc_addr = 0x3d4; // Base port of CRT Controller
const input_status_1 = 0x3da; // Input Status 1 register
const screen_width = 320;
const screen_height = 200;
const bytes_per_row = 80;
//----------------------------------------------------------------------------
// These values contain the high byte of the page's offset in display memory,
// along with the byte 0x0C, which indicates to the CRT Controller that we
// are setting the high byte of the display offset.
//
// Note that the low byte of the display page is not changed.
static int v_page_nr = 0x000C;
static int a_page_nr = 0x3f0C;
// The addresses of the active and visible screen pages.
char * v_page = screen_lin_addr + 0x0000;
char * a_page = screen_lin_addr + 0x3f00;
// The saved screen mode number.
static int old_mode_nr;
// Edge arrays.
static int edge_l [ screen_height ];
static int edge_r [ screen_height ];
//----------------------------------------------------------------------------
// The VGA object. (Only one is allowed.)
//----------------------------------------------------------------------------
vga_t vga;
//----------------------------------------------------------------------------
// FUNCTION swap
//----------------------------------------------------------------------------
inline void swap
(
int& var1,
int& var2
)
{
int temp;
temp = var1;
var1 = var2;
var2 = temp;
}
//----------------------------------------------------------------------------
// FUNCTION draw_scanlines
//----------------------------------------------------------------------------
// Draws the scan lines from y1 to y2, using the X coordinates stored in the
// edge_l and edge_r arrays.
//----------------------------------------------------------------------------
static int l_clip_plane_mask [] = { 0x0F02, 0x0E02, 0x0C02, 0x0802 };
static int r_clip_plane_mask [] = { 0x0102, 0x0302, 0x0702, 0x0F02 };
inline void draw_scanlines
(
int y1,
int y2,
int color
)
{
char * row_addr = a_page + (y1 * bytes_per_row);
for ( int i = y1; i <= y2; ++ i )
{
int x1 = edge_l [i];
int x2 = edge_r [i];
if ( x2 < x1 )
{
continue;
}
char * first_byte_addr = row_addr + x1 / 4;
char * last_byte_addr = row_addr + x2 / 4;
int l_clip = l_clip_plane_mask [x1 & 3];
int r_clip = r_clip_plane_mask [x2 & 3];
if ( first_byte_addr == last_byte_addr )
{
l_clip &= r_clip;
outpw ( sequ_addr, l_clip );
*first_byte_addr = (char) color;
}
else
{
// Draw the first byte.
outpw ( sequ_addr, l_clip );
*first_byte_addr = (char) color;
++ first_byte_addr;
// Draw the middle bytes.
outpw ( sequ_addr, 0x0F02 );
memset ( first_byte_addr, color, last_byte_addr-first_byte_addr );
// Draw the last byte.
outpw ( sequ_addr, r_clip );
*last_byte_addr = (char) color;
}
row_addr += bytes_per_row;
}
}
//----------------------------------------------------------------------------
// FUNCTION vga_t::exists
//----------------------------------------------------------------------------
// Checks to see if there is a VGA display adapter. Returns true if there
// is, false if there isn't.
//----------------------------------------------------------------------------
boolean vga_t::exists
(
void
)
{
boolean exists;
union REGS regs;
regs.h.ah = 0x1A;
regs.h.al = 0;
int386 ( video_int, & regs, & regs );
if ( regs.h.al == 0x1A )
exists = true;
else
exists = false;
return exists;
}
//----------------------------------------------------------------------------
// FUNCTION vga_t::start
//----------------------------------------------------------------------------
// Saves the current video mode and switches to mode 13h.
//----------------------------------------------------------------------------
void vga_t::start ( void )
{
union REGS regs;
// Get information about the current video state.
regs.h.ah = 0x0f;
int386 ( video_int, & regs, & regs );
old_mode_nr = regs.h.al;
// Start with normal 320x200x256 mode.
regs.x.eax = 0x13;
int386 ( video_int, & regs, & regs );
// Turn off the Chain-4 bit (bit 3 at index 4, port 0x3c4).
outpw ( sequ_addr, 0x0604 );
// Turn off word mode, by setting the Mode Control Register of the
// CRT Controller (index 0x17, port 0x3d4).
outpw ( crtc_addr, 0xE317 );
// turn off doubleword mode, by setting the Underline Location register
// (index 0x14, port 0x3d4).
outpw ( crtc_addr, 0x0014 );
// Select all four planes for output.
outpw ( sequ_addr, 0x0F02 );
// Clear display memory to zeroes.
memset ( screen_lin_addr, 0, 0x10000 );
}
//----------------------------------------------------------------------------
// FUNCTION vga_t::end
//----------------------------------------------------------------------------
// Restores the previous video mode.
//----------------------------------------------------------------------------
void vga_t::end
(
void
)
{
union REGS regs;
regs.x.eax = old_mode_nr;
int386 ( video_int, & regs, & regs );
}
//----------------------------------------------------------------------------
// FUNCTION vga_t::clear
//----------------------------------------------------------------------------
// Fills the entire screen buffer with the given color.
//----------------------------------------------------------------------------
void vga_t::clear
(
int color
)
{
// Enable writes to all four planes.
outpw ( sequ_addr, 0x0F02 );
// Clear the active page.
memset ( a_page, color, bytes_per_row * screen_height );
}
//----------------------------------------------------------------------------
// FUNCTION vga_t::update
//----------------------------------------------------------------------------
// Copies the drawing buffer into screen memory.
//----------------------------------------------------------------------------
void vga_t::update
(
void
)
{
// Swap the visible and active pages.
{
int temp = v_page_nr;
v_page_nr = a_page_nr;
a_page_nr = temp;
}
{
char * temp = v_page;
v_page = a_page;
a_page = temp;
}
// Output the Change Display Offset High byte, along with the high byte of
// the new display offset.
outpw ( crtc_addr, v_page_nr );
// Now wait for vertical sync, so the active page will be invisible when
// we start drawing to it.
#ifdef VSYNC_MONITOR
char * p = v_page;
int counter = 0;
outpw ( sequ_addr, 0x0F02 );
#endif
while ( (inp (input_status_1) & 8) != 0 )
;
while ( (inp (input_status_1) & 8) == 0 )
{
// Do nothing.
#ifdef VSYNC_MONITOR
++ counter;
if ( counter >= 128 )
{
counter = 0;
*p = 255;
p += bytes_per_row-1;
*p = 255;
++ p;
}
#endif
}
}
//----------------------------------------------------------------------------
// FUNCTION win_t::resize
//----------------------------------------------------------------------------
// Resizes the clipping window to the given coordinates.
//----------------------------------------------------------------------------
void win_t::resize
(
int new_x1,
int new_y1,
int new_x2,
int new_y2
)
{
x1 = new_x1;
y1 = new_y1;
x2 = new_x2;
y2 = new_y2;
}
//----------------------------------------------------------------------------
// FUNCTION win_t::clear
//----------------------------------------------------------------------------
// Fills a window with the given color.
//----------------------------------------------------------------------------
void win_t::clear
(
int color
)
{
int y;
// Scan convert the window.
for ( y = y1; y < y2; ++ y )
{
edge_l [y] = x1;
edge_r [y] = x2-1;
}
// Fill the window.
draw_scanlines ( y1, y2-1, color );
}
//----------------------------------------------------------------------------
// FUNCTION win_t::point
//----------------------------------------------------------------------------
// Draws a point that is clipped to the given window.
//----------------------------------------------------------------------------
void win_t::point
(
int x,
int y,
int color
)
{
if ( x >= x1 && x < x2 && y >= y1 && y < y2 )
{
outp ( sequ_addr, 0x02 );
outp ( sequ_addr+1, 0x01 << (x & 3) );
a_page [ bytes_per_row*y + (x / 4) ] = (char) color;
}
}
//----------------------------------------------------------------------------
// FUNCTION win_t::rect
//----------------------------------------------------------------------------
// Draws a filled rectangle that is clipped to the given window.
//----------------------------------------------------------------------------
void win_t::rect
(
int rect_x1,
int rect_y1,
int rect_x2,
int rect_y2,
int color
)
{
int y;
// Clip the rectangle to the viewing window.
if ( rect_x2 < x1 || rect_x1 >= x2 ||
rect_y2 < y1 || rect_y1 >= y2 )
goto exit_func;
if ( rect_x1 < x1 )
rect_x1 = x1;
if ( rect_x2 > x2 )
rect_x2 = x2;
if ( rect_y1 < y1 )
rect_y1 = y1;
if ( rect_y2 > y2 )
rect_y2 = y2;
// Scan convert the rectangle.
rect_x2 -= 1;
rect_y2 -= 1;
for ( y = rect_y1; y <= rect_y2; ++ y )
{
edge_l [y] = rect_x1;
edge_r [y] = rect_x2;
}
// Draw!
draw_scanlines ( rect_y1, rect_y2, color );
exit_func:
return;
}
//----------------------------------------------------------------------------
// FUNCTION win_t::line
//----------------------------------------------------------------------------
// Draws a line that is clipped to the given window.
//----------------------------------------------------------------------------
void win_t::line
(
int line_x1,
int line_y1,
int line_x2,
int line_y2,
int color
)
{
int delta_x;
int delta_y;
// Switch the points so that (line_x1,line_y1) is always the top one.
if ( line_y2 < line_y1 )
{
swap ( line_x1, line_x2 );
swap ( line_y1, line_y2 );
}
// Compute the x and y deltas.
delta_x = line_x2 - line_x1;
delta_y = line_y2 - line_y1;
// Clip the line on the left and right edges.
if ( line_x1 < x1 )
{
if ( line_x2 < x1 ) goto exit_func;
line_y1 += ( delta_y * ( x1 - line_x1 ) / delta_x );
line_x1 = x1;
}
else if ( line_x1 >= x2 )
{
if ( line_x2 >= x2 ) goto exit_func;
line_y1 += ( delta_y * ( x2 - line_x1 ) / delta_x );
line_x1 = x2 - 1;
}
if ( line_x2 < x1 )
{
line_y2 += ( delta_y * ( x1 - line_x2 ) / delta_x );
line_x2 = x1;
}
else if ( line_x2 >= x2 )
{
line_y2 += ( delta_y * ( x2 - line_x2 ) / delta_x );
line_x2 = x2 - 1;
}
// Don't draw lines that are completely off the screen.
if ( line_y2 < y1 || line_y1 >= y2 ) goto exit_func;
// Clip the line on the top edge.
if ( line_y1 < y1 )
{
line_x1 += ( delta_x * ( y1 - line_y1 ) / delta_y );
line_y1 = y1;
if ( line_x1 < x1 ) line_x1 = x1;
if ( line_x1 >= x2 ) line_x1 = x2 - 1;
}
// Clip the line on the bottom edge.
if ( line_y2 >= y2 )
{
line_x2 += ( delta_x * ( y2 - line_y2 ) / delta_y );
line_y2 = y2 - 1;
if ( line_x2 < x1 ) line_x2 = x1;
if ( line_x2 >= x2 ) line_x2 = x2 - 1;
}
// Recompute the x and y deltas using the clipped coordinates.
delta_x = line_x2 - line_x1;
delta_y = line_y2 - line_y1;
// Draw the line.
{
int denominator;
int nr_pixels;
int frac_x;
int frac_y;
int add_x;
const int add_y = 1;
if (delta_x > 0)
{
add_x = 1;
}
else
{
add_x = -1;
delta_x = -delta_x;
}
if ( delta_y > delta_x )
{
denominator = delta_y;
nr_pixels = delta_y + 1;
}
else
{
denominator = delta_x;
nr_pixels = delta_x + 1;
}
frac_y = frac_x = denominator >> 1;
for ( ; nr_pixels > 0; -- nr_pixels )
{
point ( line_x1, line_y1, color );
frac_x += delta_x;
if ( frac_x > denominator )
{
frac_x -= denominator;
line_x1 += add_x;
}
frac_y += delta_y;
if ( frac_y > denominator )
{
frac_y -= denominator;
line_y1 += add_y;
}
}
}
exit_func:
return;
}